﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Fetches.Fetch.Dto;
using HttpClient = System.Net.Http.HttpClient;

namespace Fetches.Fetch.Fetcher
{
    /// <summary>
    /// HttpClient的Cookie默认会忽略自己设置的Cookie，而使用网站交互产生的Cookie
    /// 建议每个HttpClientFetcher使用登录方法自行产生自己的Cookie
    /// </summary>
    public class HttpClientFetcher : AbstractFetcher
    {
        private HttpClient _httpClient;


        public override void CustomInitialize()
        {
            _httpClient = new HttpClient
            {
                Timeout = TimeSpan.FromSeconds(Order.RequestTimeout)
            };
        }

        protected override ResponsePage Fetch(Request request)
        {
            try
            {
                var httpRequest = GenerateHttpRequestMessage(request);

                var stopwatch = new Stopwatch();
                stopwatch.Start();
                var httpResponseMessage = _httpClient.SendAsync(httpRequest).Result;
                stopwatch.Stop();

                var response = new ResponsePage()
                {
                    ElapsedMilliseconds = (int)stopwatch.ElapsedMilliseconds,
                    StatusCode = (int)httpResponseMessage.StatusCode
                };
                foreach (var header in httpResponseMessage.Headers)
                {
                    response.Headers.Add(header.Key, new HashSet<string>(header.Value));
                }

                Encoding encoding;
                if (string.IsNullOrEmpty(Order.Charset))
                {
                    var responseCharset = httpResponseMessage.Content.Headers.ContentType.CharSet;
                    if (!string.IsNullOrEmpty(responseCharset))
                    {
                        encoding = Encoding.GetEncoding(responseCharset);
                        Order.Charset = responseCharset;
                    }
                    else
                    {
                        byte[] bytes = { };
                        try
                        {
                            bytes = httpResponseMessage.Content.ReadAsByteArrayAsync().Result;
                            var content = Encoding.Default.GetString(bytes);
                            Match charSetMatch = Regex.Match(content, "<meta([^<]*)charset=([^<]*)\"", RegexOptions.IgnoreCase | RegexOptions.Multiline);
                            string webCharSet = charSetMatch.Groups[2].Value;
                            encoding = Encoding.GetEncoding(webCharSet);
                            Order.Charset = webCharSet;
                        }
                        catch (Exception)
                        {
                            encoding = Encoding.Default;
                        }

                        response.SourceCode = encoding.GetString(bytes);
                    }

                }
                else
                {
                    encoding = Encoding.GetEncoding(Order.Charset);
                }

                if (string.IsNullOrEmpty(response.SourceCode))
                {
                    using (var sr = new StreamReader(httpResponseMessage.Content.ReadAsStreamAsync().Result, encoding))
                    {
                        response.SourceCode = sr.ReadToEnd();
                    }
                }

                foreach (var header in httpResponseMessage.Content.Headers)
                {
                    response.Headers.Add(header.Key, new HashSet<string>(header.Value));
                }

                return response;

            }
            catch (Exception e)
            {
                return new ResponsePage
                {
                    Request = request,
                    StatusCode = (int)HttpStatusCode.BadGateway,
                    SourceCode = e.ToString()
                };
            }
        }

        protected override Task<byte[]> GetResourceAsync(string refer, Uri resourceUri)
        {
            _httpClient.DefaultRequestHeaders.Referrer=new Uri(refer);
           return _httpClient.GetByteArrayAsync(resourceUri);
        }


        private HttpRequestMessage GenerateHttpRequestMessage(Request request)
        {
            var httpRequestMessage =
                new HttpRequestMessage(
                    string.IsNullOrWhiteSpace(request.Method)
                        ? HttpMethod.Get
                        : new HttpMethod(request.Method.ToUpper()),
                    request.Url);

            foreach (var header in request.Headers)
            {
                httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }

            if (string.IsNullOrWhiteSpace(request.UserAgent))
            {
                httpRequestMessage.Headers.TryAddWithoutValidation("User-Agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36 Edg/80.0.361.69");
            }

            if (request.Method.ToUpper() == "POST")
            {
                if (request.PostData != null)
                {
                    if (request.PostDataType == "binnary")
                    {
                        httpRequestMessage.Content = new System.Net.Http.ByteArrayContent(request.PostData);

                    }
                    else if (request.PostDataType == "string")
                    {
                        httpRequestMessage.Content = new System.Net.Http.StringContent(Encoding.UTF8.GetString(request.PostData));
                    }

                    foreach (var header in request.Headers)
                    {
                        httpRequestMessage.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
                    }
                }

            }

            return httpRequestMessage;
        }
    }
}